/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#include "pch.h"
#include <baja/stream/storage_file.h>
#include <journal/models/photo_model_factory.h>
#include <journal/models/photo_model.h>
#include <journal/models/db/photo_table.h>
#include <journal/models/db/media_file_table.h>

using namespace journal::model;

b::uint32 photo_model_factory::_typeToken = 0;

photo_model_factory::photo_model_factory(std::shared_ptr<s::iengine> dbEngine) :
    _nextId(0),
    _dbEngine(dbEngine)
{
    if (_typeToken == 0)
    {
        _typeToken = constants::undoRedoService()->generateTargetTypeToken();

        constants::memoryTrackingService()->registerType(_typeToken, L"photo model", true);

        DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_GENERIC_MODEL,
            boost::wformat(L"photo_model_factory::photo_model_factory() : type token = %i") %
            _typeToken
            );
    }
}

std::shared_ptr<iphoto_model_factory> photo_model_factory::create(std::shared_ptr<s::iengine> dbEngine)
{
    static bool instantiated = false;
    static std::shared_ptr<photo_model_factory> returnValue = std::make_shared<photo_model_factory>(dbEngine);
    static std::recursive_mutex mutex;
    BAJA_LOCK(mutex);
    if (instantiated == false)
    {
        returnValue->_photoTable = journal::model::db::createPhotoTable(dbEngine);
        returnValue->_photoTable->common()->initialize();
        returnValue->_photoTable->disposeInactiveRecords();
        returnValue->_nextId = returnValue->_photoTable->common()->getNextId();

        returnValue->_mediaFileTable = journal::model::db::createMediaFileTable(dbEngine);
        returnValue->_mediaFileTable->common()->initialize();

        // this must occur after initializing both tables.
        returnValue->disposeUnusedFiles();

        instantiated = true;
    }
    return returnValue;
}

void photo_model_factory::disposeUnusedFiles()
{
    std::set<b::uint32> mediaFileIdsInUse;
    _photoTable->getMediaFileIds(mediaFileIdsInUse);

    for (auto &id : mediaFileIdsInUse)
    {
        DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_MODEL,
            boost::wformat(L"photo_model_factory::disposeUnusedFiles() : media file id = %i") %
            id
            );
    }

    std::vector<std::wstring> filesToDispose;
    _mediaFileTable->disposeUnused(mediaFileIdsInUse, filesToDispose);

    std::wstring targetFolder(Windows::Storage::ApplicationData::Current->LocalFolder->Path->Data());

    for (auto &fullFileName : filesToDispose)
    {
        DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_MODEL,
            boost::wformat(L"photo_model_factory::disposeUnusedFiles() : deleting = %s") %
            fullFileName
            );

        std::shared_ptr<b::storage::istorage_file> file = b::storage::createStorageFile();
        file->initializeAsync(fullFileName,
                [file](bool r1)
                {
                    if (!r1)
                    {
                        BAJA_THROW(std::logic_error("an error occured while getting a handle on a file"));
                    }
                    file->deleteAsync(
                            [](bool r2)
                            {
                                if (!r2)
                                {
                                    BAJA_THROW(std::logic_error("an error occured while deleting a file"));
                                }
                            }
                        );
                }
        );
    }
}

b::uint32 photo_model_factory::typeToken()
{
    return _typeToken;
}

void photo_model_factory::getPersistedPhotoIds(b::uint32 pageId, std::vector<b::uint32>& ids)
{
    _photoTable->readActiveIds(pageId, ids);
}

std::shared_ptr<iphoto_model> photo_model_factory::createPhoto(
    const std::shared_ptr<ipage_model>& parent)
{
    return journal::model::createPhoto(_typeToken, parent, _photoTable, _mediaFileTable, _nextId++);
}

std::shared_ptr<iphoto_model> photo_model_factory::recreatePhoto(
    const std::shared_ptr<ipage_model>& parent, 
    const b::uint32& photoId)
{
    return journal::model::createPhoto(_typeToken, parent, _photoTable, _mediaFileTable, photoId);
}

void photo_model_factory::removeFromDB(
        const b::uint32& pageId
        )
{
    journal::model::removePhotos(_photoTable, pageId);
}

std::shared_ptr<iphoto_model_factory> journal::model::createPhotoModelFactory(std::shared_ptr<s::iengine> dbEngine)
{
    return photo_model_factory::create(dbEngine);
}
